package edu.pucsp.predatorprey.simulador.java.criatura;

import edu.pucsp.predatorprey.simulador.java.posicao.impl.Direcao;
import edu.pucsp.predatorprey.simulador.java.posicao.impl.Posicao;
import edu.pucsp.predatorprey.simulador.java.terreno.api.Terreno;
import edu.pucsp.predatorprey.simulador.java.terreno.impl.ListaPosicao;

public abstract class AbstractCriatura implements Runnable {

	protected Terreno terreno;

	protected int ciclo = 0;

	private int cicloProcriacao = 0;

	private int cicloMovimento = 0;

	public void definirTerreno(Terreno terreno) {
		this.terreno = terreno;
	}

	protected abstract Direcao getDirecao();

	protected abstract int getIntervalorProcriacao();

	protected abstract int getIntervaloMovimento();

	public abstract boolean isPredador();

	public abstract boolean deveMorerCausasNaturais();

	@Override
	public void run() {
		if (this.ciclo - this.cicloMovimento >= this.getIntervaloMovimento()) {
			this.terreno.moverCriatura(this, this.getDirecao());
			this.cicloMovimento = this.ciclo;
		}
		this.ciclo++;
	}

	public boolean podeComer(AbstractCriatura criatura) {
		return this.isPredador() && !criatura.isPredador();
	}

	public boolean podeProcriar(AbstractCriatura criatura) {
		if (this.getClass().equals(criatura.getClass())) {
			return this.aptoParaProcriar() && criatura.aptoParaProcriar();
		}
		return false;
	}

	public void procriou() {
		this.cicloProcriacao = this.ciclo;
	}

	public abstract void comeu();

	protected boolean aptoParaProcriar() {
		return this.ciclo - this.cicloProcriacao >= this.getIntervalorProcriacao();
	}

	protected Direcao getDirecaoAleatoria() {
		Direcao[] direcoes = Direcao.values();
		return direcoes[(int) (Math.random() * direcoes.length)];
	}

	protected Direcao getDirecaoPresa(int distancia) {
		return this.getDirecaoCriatura(this.terreno.obterPosicoesRelativasPresas(this), distancia);
	}

	protected Direcao getDirecaoPredador(int distancia) {
		return this.getDirecaoCriatura(this.terreno.obterPosicoesRelativasPredadores(this), distancia);
	}

	private Direcao getDirecaoCriatura(ListaPosicao posicoes, int distancia) {
		if (posicoes != null) { // Criatura já morreu
			Posicao posicaoMaisProxima = this.getPosicaoMasProxima(posicoes, distancia);
			if (posicaoMaisProxima != null && posicaoMaisProxima.distanciaAte(new Posicao(0, 0)) >= 2) {
				int x = posicaoMaisProxima.getX();
				int y = posicaoMaisProxima.getY();
				if (Math.abs(x) > Math.abs(y)) {
					if (x > 0) {
						return Direcao.LESTE;
					} else {
						return Direcao.OESTE;
					}
				} else {
					if (y > 0) {
						return Direcao.SUL;
					} else {
						return Direcao.NORTE;
					}
				}
			}
		}
		return null;
	}

	/**
	 * @param posicoesRelativas
	 * @param distancia
	 * @return
	 */
	private Posicao getPosicaoMasProxima(ListaPosicao posicoesRelativas, int distanciaMaxima) {
		Posicao posicaoBase = new Posicao(0, 0);
		Posicao menor = null;
		double menorDistancia = distanciaMaxima;
		for (Posicao pos : posicoesRelativas) {
			double distancia = posicaoBase.distanciaAte(pos);
			if (distancia < menorDistancia && distancia != 0) {
				menor = pos;
				menorDistancia = distancia;
			}
		}
		return menor;
	}

}
